package org.jingouzhui.infrastructure.persistent.repository;

import lombok.extern.slf4j.Slf4j;
import org.jingouzhui.domain.strategy.model.entity.StrategyAwardEntity;
import org.jingouzhui.domain.strategy.model.entity.StrategyEntity;
import org.jingouzhui.domain.strategy.model.entity.StrategyRuleEntity;
import org.jingouzhui.domain.strategy.model.valboj.*;
import org.jingouzhui.domain.strategy.repository.IStrategyRepository;
import org.jingouzhui.infrastructure.persistent.dao.*;
import org.jingouzhui.infrastructure.persistent.po.*;
import org.jingouzhui.infrastructure.persistent.redis.IRedisService;
import org.jingouzhui.types.common.Constants;
import org.jingouzhui.types.common.Delimiter;
import org.jingouzhui.types.exception.AppException;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static org.jingouzhui.types.common.Constants.RedisKey.*;

/**
 * @description: 策略仓储层实现
 * @author: jingouzhui
 * @date: 2025/4/24 14:37
 */
@Repository
@Slf4j
public class StrategyRepository implements IStrategyRepository {
    @Resource
    private IRedisService redisService;
    @Resource
    private IStrategyAwardDAO strategyAwardDAO;
    @Resource
    private IStrategyDAO strategyDAO;
    @Resource
    private IStrategyRuleDAO strategyRuleDAO;

    @Resource
    private IRuleTreeDAO ruleTreeDAO;
    @Resource
    private IRuleTreeNodeDAO ruleTreeNodeDAO;
    @Resource
    private IRuleTreeNodeLineDAO ruleTreeNodeLineDAO;

    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {

        String cacheKey = STRATEGY_AWARD_KEY + strategyId;
        //先查缓存
        List<StrategyAwardEntity> strategyAwardEntities = redisService.getValue(cacheKey);
        if (null != strategyAwardEntities && ! strategyAwardEntities.isEmpty()) {
            return strategyAwardEntities;
        }
        //没有就查库
        List<StrategyAward> strategyAwardList = strategyAwardDAO.queryStrategyAwardListByStrategyId(strategyId);
        if (CollectionUtils.isEmpty(strategyAwardList)) {
            return new ArrayList<>();
        }
        strategyAwardEntities = new ArrayList<>(strategyAwardList.size());
        StrategyAwardEntity entity;
        for (StrategyAward strategyAward : strategyAwardList) {
            entity = new StrategyAwardEntity();
            BeanUtils.copyProperties(strategyAward,entity);
            strategyAwardEntities.add(entity);
        }
        //重新填充缓存
        redisService.setValue(cacheKey,strategyAwardEntities);
        return strategyAwardEntities;
    }

    @Override
    public void storeStrategyAwardSearchRateTables(String strategyId, Integer totalRange, Map<Integer, Integer> shuffleStrategyAwardSearchRateMap) {
        //1.存储抽奖策略范围值，如10000，用于生成0-9999的范围值
        redisService.setValue(STRATEGY_RATE_RANGE_KEY + strategyId, totalRange);
        //存储概率查找表
        Map<Integer, Integer> cacheRateTable = redisService.getMap(STRATEGY_RATE_TABLE_KEY + strategyId);
        cacheRateTable.putAll(shuffleStrategyAwardSearchRateMap);
    }

    @Override
    public Integer getRateRange(Long strategyId) {
        return getRateRange(String.valueOf(strategyId));
    }

    @Override
    public Integer getRateRange(String key) {
        return redisService.getValue(STRATEGY_RATE_RANGE_KEY + key);
    }

    @Override
    public Integer getStrategyAwardAssemble(String strategyId, int rateKey) {
       return redisService.getFromMap(STRATEGY_RATE_TABLE_KEY + strategyId,rateKey);
    }

    @Override
    public StrategyEntity queryStrategyByStrategyId(Long strategyId) {
        StrategyEntity strategyEntity = new StrategyEntity();
        Strategy strategy = strategyDAO.queryStrategyByStrategyId(strategyId);
        BeanUtils.copyProperties(strategy,strategyEntity);
        return strategyEntity;
    }

    @Override
    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) {
        StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
        StrategyRule strategyRuleReq = new StrategyRule();
        strategyRuleReq.setRuleModel(ruleModel);
        strategyRuleReq.setStrategyId(strategyId);
        StrategyRule strategyRule =  strategyRuleDAO.queryStrategyRule(strategyRuleReq);
       BeanUtils.copyProperties(strategyRule,strategyRuleEntity);
        return strategyRuleEntity;
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
        StrategyRule strategyRule = StrategyRule.builder()
                .ruleModel(ruleModel)
                .awardId(awardId)
                .strategyId(strategyId)
                .build();
        return strategyRuleDAO.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
        return queryStrategyRuleValue(strategyId,null,ruleModel);
    }

    @Override
    public StrategyAwardRuleModelVO queryRuleModelByStrategyIdAndAwardId(Long strategyId, Integer awardId) {
        StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyId)
                .awardId(awardId)
                .build();
        String ruleModels  = strategyAwardDAO.queryRuleModelByStrategyIdAndAwardId(strategyAwardEntity);
        return StrategyAwardRuleModelVO.builder()
                .ruleModels(ruleModels)
                .build();
    }

    @Override
    public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {
        String cacheKey = STRATEGY_RULE_TREE_VO_KEY + treeId;
        RuleTreeVO value = redisService.getValue(cacheKey);
        if (Objects.nonNull(value)) {
            return value;
        }
        //查询数据库
        RuleTree ruleTree = ruleTreeDAO.queryRuleTreeByTreeId(treeId);
        if (Objects.isNull(ruleTree)) {
            throw  new AppException("输入的TreeId有误:"+treeId);
        }
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDAO.queryRuleTreeNodeByTreeId(treeId);
        List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineDAO.queryRuleTreeNodeLineByTreeId(treeId);
        //构建 ruleTreeNodeLineMap
        Map<String,List<RuleTreeNodeLineVO>> ruleTreeNodeLineVOMap = new HashMap<>();
        for (RuleTreeNodeLine ruleTreeNodeLine : ruleTreeNodeLines) {
            RuleTreeNodeLineVO ruleTreeNodeLineVO = RuleTreeNodeLineVO.builder()
                    .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
                    .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
                    .ruleLimitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                    .ruleLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
                    .treeId(ruleTreeNodeLine.getTreeId())
                    .build();
            List<RuleTreeNodeLineVO> ruleTreeNodeLineVOList = ruleTreeNodeLineVOMap.computeIfAbsent(ruleTreeNodeLine.getRuleNodeFrom(), k -> new ArrayList<>());
            ruleTreeNodeLineVOList.add(ruleTreeNodeLineVO);
        }
            //构建 nodeMap
            Map<String,RuleTreeNodeVO> nodeMap = new HashMap<>();
            for (RuleTreeNode ruleTreeNode : ruleTreeNodes) {
                RuleTreeNodeVO ruleTreeNodeVO = RuleTreeNodeVO.builder()
                        .ruleKey(ruleTreeNode.getRuleKey())
                        .ruleDesc(ruleTreeNode.getRuleDesc())
                        .treeId(ruleTreeNode.getTreeId())
                        .ruleValue(ruleTreeNode.getRuleValue())
                        .ruleTreeNodeLineVOList(ruleTreeNodeLineVOMap.get(ruleTreeNode.getRuleKey()))
                        .build();
                nodeMap.put(ruleTreeNode.getRuleKey(),ruleTreeNodeVO);
            }
            //构建ruleTree
            RuleTreeVO ruleTreeVO = RuleTreeVO.builder()
                    .treeId(ruleTree.getTreeId())
                    .treeName(ruleTree.getTreeName())
                    .treeDesc(ruleTree.getTreeDesc())
                    .treeRootRuleNode(ruleTree.getTreeRootRuleKey())
                    .treeNodeMap(nodeMap)
                    .build();
            redisService.setValue(cacheKey,ruleTreeVO);
        return  ruleTreeVO;
    }

    @Override
    public void storeAwardStockCount(String cacheKey, Long awardCountSurplus) {
        if (redisService.isExists(cacheKey)) return;
        redisService.setAtomicLong(cacheKey, awardCountSurplus);
    }

    @Override
    public Boolean subtractionAwardStock(String cacheKey) {
        long surplus = redisService.decr(cacheKey);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            redisService.setValue(cacheKey, 0);
            return false;
        }
        // 1. 按照cacheKey decr 后的值，如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底，如果后续有恢复库存，手动处理等，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = cacheKey + Delimiter.UNDERLINE + surplus;
        Boolean lock = redisService.setNx(lockKey);
        if (!lock) {
            log.info("策略奖品库存加锁失败 {}", lockKey);
        }
        return lock;
    }

    @Override
    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) {
        String cacheKey = STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<StrategyAwardStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(strategyAwardStockKeyVO,3, TimeUnit.SECONDS);
    }

    @Override
    public StrategyAwardStockKeyVO takeQueueValue() throws InterruptedException {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
        return destinationQueue.poll();
    }

    @Override
    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        strategyAwardDAO.updateStrategyAwardStock(strategyAward);
    }
}
